import type { $context, ContextHttp, Results, Logger, $meta } from "../index.ts";

export interface $events {
    "*": { key: keyof $events, value: any };
    "milkio:httpRequest": { executeId: string; path: string; logger: Logger; http: ContextHttp<Record<string, any>> };
    "milkio:httpResponse": { executeId: string; path: string; logger: Logger; http: ContextHttp<Record<string, any>>; context: $context; success: boolean };
    "milkio:httpNotFound": { executeId: string; path: string; logger: Logger; http: ContextHttp<Record<string, any>> };
    "milkio:executeBefore": { executeId: string; path: string; logger: Logger; meta: $meta; context: $context };
    "milkio:executeAfter": { executeId: string; path: string; logger: Logger; meta: $meta; context: $context; results: Results<any> };
}

export function __initEventManager() {
    const handlers = new Map<(event: any) => void, string>();
    const indexed = new Map<string, Set<(event: any) => Promise<void | boolean> | void | boolean>>();

    const eventManager = {
        on: <Key extends keyof $events, Handler extends (event: $events[Key]) => Promise<void | boolean> | void | boolean>(key: Key, handler: Handler) => {
            handlers.set(handler, key as string);
            if (key === '*') {
                if (indexed.has('*') === false) {
                    indexed.set('*', new Set());
                }
                const wildcardSet = indexed.get('*')!;
                wildcardSet.add(handler);
            } else {
                if (indexed.has(key as string) === false) {
                    indexed.set(key as string, new Set());
                }
                const set = indexed.get(key as string)!;
                set.add(handler);
            }

            return () => {
                handlers.delete(handler);
                if (key === '*') {
                    const wildcardSet = indexed.get('*');
                    if (wildcardSet) {
                        wildcardSet.delete(handler);
                    }
                } else {
                    const set = indexed.get(key as string);
                    if (set) {
                        set.delete(handler);
                    }
                }
            };
        },
        off: <Key extends keyof $events, Handler extends (event: $events[Key]) => void>(key: Key, handler: Handler) => {
            if (key === '*') {
                const wildcardSet = indexed.get('*');
                if (!wildcardSet) return;
                handlers.delete(handler);
                wildcardSet.delete(handler);
            } else {
                const set = indexed.get(key as string);
                if (!set) return;
                handlers.delete(handler);
                set.delete(handler);
            }
        },
        emit: async <Key extends keyof $events, Value extends $events[Key]>(key: Key, value: Value): Promise<void> => {
            const wildcardHandlers = indexed.get('*');
            if (wildcardHandlers) {
                for (const handler of wildcardHandlers) {
                    await handler({ key, value });
                }
            }

            const h = indexed.get(key as string);
            if (h) {
                for (const handler of h) {
                    await handler(value);
                }
            }
        },
        emitAnyApproved: async <Key extends keyof $events, Value extends $events[Key]>(key: Key, value: Value): Promise<boolean> => {
            const wildcardHandlers = indexed.get('*');
            let accepted = false;
            if (wildcardHandlers) {
                for (const handler of wildcardHandlers) {
                    if ((await handler({ key, value })) === true) {
                        accepted = true;
                    }
                }
            }

            const h = indexed.get(key as string);
            if (h) {
                for (const handler of h) {
                    if ((await handler(value)) === true) {
                        accepted = true;
                    }
                }
            }
            return accepted;
        },
        emitAllApproved: async <Key extends keyof $events, Value extends $events[Key]>(key: Key, value: Value): Promise<boolean> => {
            const wildcardHandlers = indexed.get('*');
            let approved = true;
            if (wildcardHandlers) {
                for (const handler of wildcardHandlers) {
                    if ((await handler({ key, value })) !== true) {
                        approved = false;
                    }
                }
            }

            const h = indexed.get(key as string);
            if (h) {
                for (const handler of h) {
                    if ((await handler(value)) !== true) {
                        approved = false;
                    }
                }
            }
            return approved;
        },
    };

    return eventManager;
}
